#include <string>
#include <sstream>
#include "debug.h"
#include "enums.h"
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>        
#include <boost/thread.hpp>
#include "StateMachine.h"
#include "NclCache.h"


/// class Ncl - 
class Ncl {
protected:
   	debug 				dbg;

	long 				id;
	string 				str_id;
	static long 			next_id;

	MediaType 			type;
	vector<Ncl*> 			media;
	vector<boost::thread*> 		trd;

	boost::shared_ptr<NclCache> 	ncl_cache;
	string 				ncl_file;
    	StateMachine			ncl_fsm;

  // Operations
public:
	// In the first ncl object creation the code is interpreted
 	Ncl(char* file):str_id(to_string(next_id)),type(MEDIA_NCL),ncl_file(file),ncl_cache(new NclCache(str_id, file)),ncl_fsm(this)
	{
		init();
		start();
		interpretPorts();
	}

	// In others ncl object creation the media is started or not
  	Ncl(bool flag_start):type(MEDIA_NCL),ncl_fsm(this)
	{
		init();
		init_state(flag_start);
	}

	// In others ncl object creation the media is started or not
  	Ncl(string id ,boost::shared_ptr<NclCache>& cache):type(MEDIA_NCL),str_id(id),ncl_cache(cache),ncl_fsm(this)
	{
		init();
	}

	void init()
	{
		if(str_id.compare("")==0)
		{
			str_id=to_string(next_id);
		}

		id=next_id;
		dbg.log(__PRETTY_FUNCTION__, "creating instance[" + str_id + "]...");
		next_id++;
	}

	~Ncl()
	{
		deleteAllMedias();
		deleteAllThreads();
		stop();

		dbg.log(__PRETTY_FUNCTION__, "destroying instance[" + str_id + "].");
		dbg.log(__PRETTY_FUNCTION__, "############[End instance]##########");
	}
	
	string getId()
	{
		return str_id;
	}
	string to_string(long i)
	{
		return boost::lexical_cast<string>(i);	
	}

  	void start ()
	{
		dbg.log(__PRETTY_FUNCTION__, "[" + str_id + "]starting...");
    		ncl_fsm.process_event(START);      
	}
	void stop ()
	{
		dbg.log(__PRETTY_FUNCTION__, "[" + str_id + "]stoping...");
    		ncl_fsm.process_event(STOP);      
	}
	void pause ()
	{
		dbg.log(__PRETTY_FUNCTION__, "[" + str_id + "]pausing...");
    		ncl_fsm.process_event(PAUSE);      
	}
	void resume ()
	{
		dbg.log(__PRETTY_FUNCTION__, "[" + str_id + "]resuming...");
    		ncl_fsm.process_event(RESUME);      
	}
	void abort ()
	{
		dbg.log(__PRETTY_FUNCTION__, "[" + str_id + "]aborting...");
    		ncl_fsm.process_event(ABORT);      
	}
	int  getState ()
	{
		dbg.log(__PRETTY_FUNCTION__, "current state:" + ncl_fsm.getState());
	}

	bool interpretTransition(string t);
	bool interpretPorts ();
	bool assign ()
	{
		dbg.log(__PRETTY_FUNCTION__, "assigning property...");
	}
	
	void init_state(bool flag)
	{
		if(flag==true)
		{
			start();
		}
		else
		{
			stop();
		}
	}

	void addMedia(Ncl* m)
	{
		media.push_back( m );
	}


	template<typename T>
	void addMedia(vector<boost::thread*> &t, string id)
	{
		media.push_back( new T(id, ncl_cache) );
		int i=media.size()-1;
		t.push_back(new boost::thread(boost::bind(&T::start,(T*)media[i])));
	}

	
	void startMedias()
	{
		startThreads();
	}

	void startThreads()
	{
		for(int i=0;i<trd.size();i++)
		{
			trd[i]->join();
		}
	}

	void deleteAllMedias()
	{
		// destroing all created elements!
		BOOST_FOREACH( Ncl* m, media )
		{	
			delete m;
		}

		// clear Ncl vector
		media.clear();
	}

	void deleteAllThreads()
	{
		// destroing all created elements!
		BOOST_FOREACH( boost::thread* t, trd )
		{	
			delete t;
		}

		// clear Ncl vector
		trd.clear();
	}

	
};

